import * as path from 'path';

import { expect } from '@jest/globals';
import * as execa from 'execa';
import * as fse from 'fs-extra';

import { getJson5Obj } from '../../../../src/utils/json-util';
import { runHvigorAsync } from '../../common/common';
import { commonConfig } from '../../plugins/common-config';
import { cleanTarget, commonFileOperation } from '../../plugins/common-fun';
import {
  AbilityType,
  APIType,
  APIVersion,
  CaseLevel,
  LanguageType,
  ModuleTemplateType,
  RuntimeOSType,
  SDKType,
} from '../../plugins/project-builder/models/constants';
import { Project } from '../../plugins/project-builder/models/project-models';
import { ICaseRecord } from '../../plugins/report-builder/report-models';
import { TestCaseBuilder } from '../../plugins/task-builder/task-case-builder';
import { ProjectBuildOption, TestCaseFun } from '../../plugins/task-builder/test-case';
import { TestTaskOptionBuilder } from '../../plugins/task-builder/test-task-option';

const projectBuilderOption: ProjectBuildOption = {
  template: {
    sdk: SDKType.Single,
    runtimeOS: RuntimeOSType.HarmonyOS,
    apiVersion: APIVersion.API11,
    apiType: APIType.STAGE,
    language: LanguageType.ArkTs,
    abilityType: AbilityType.Application,
    entryTemplateType: ModuleTemplateType.EmptyAbility,
  },
  modulesOption: {
    [ModuleTemplateType.StaticLibrary]: 2,
    [ModuleTemplateType.SharedLibrary]: 1,
  },
};

const BUILD_PACKAGE_JSON_PATH = 'build/default/cache/default/default@PackageHar/oh-package.json5';

async function initPackageAndProfile(root: string, buildOption: Record<string, any>, dependencies: Record<string, string> | undefined = undefined) {
  if (dependencies) {
    const harPackagePath = path.resolve(root, 'oh-package.json5');
    const harPackageJson = getJson5Obj(harPackagePath);
    harPackageJson.dependencies = dependencies;
    fse.writeFileSync(harPackagePath, JSON.stringify(harPackageJson, null, 2));
  }

  const harProfilePath = path.resolve(root, 'build-profile.json5');
  const harProfile = getJson5Obj(harProfilePath);
  harProfile.buildOption = buildOption;
  fse.writeFileSync(harProfilePath, JSON.stringify(harProfile, null, 2));
}

const testFun: TestCaseFun = async (project, taskConfig, record) => {
  const projectRoot = project.projectRootPath();
  const projectProfilePath = path.resolve(projectRoot, 'build-profile.json5');
  const projectProfile = getJson5Obj(projectProfilePath);
  projectProfile.app.products[0].buildOption = { strictMode: { caseSensitiveCheck: true, useNormalizedOHMUrl: true } };
  fse.writeFileSync(projectProfilePath, JSON.stringify(projectProfile, null, 2));

  // 1.验证har依赖dayjs，开启bundledDependencies，需收集dayjs到extraImportees中
  const har1Root = project.getModule('staticLibrary1').moduleRootPath();
  await initPackageAndProfile(har1Root, { arkOptions: { bundledDependencies: true } }, { dayjs: 'latest' });

  const har1MainPagePath = path.resolve(har1Root, 'src/main/ets/components/mainpage/MainPage.ets');
  await commonFileOperation(har1MainPagePath, async (buffer) => {
    return `import dayjs from 'dayjs';\nimport utc from 'dayjs/plugin/utc';\n${buffer.toString().replace("'Hello World'", "'Hello World' + typeof dayjs + typeof utc")}`;
  });
  // 执行ohpm install
  const install = await execa.command(commonConfig.ohpmInstallCommand, {
    cwd: har1Root,
  });
  record.addStep('har1 install', install);

  const commands = '--mode module -p module=staticLibrary1@default assembleHar -p buildMode=release'.split(' ');
  const result = await runHvigorAsync(projectRoot, commands);
  record.addStep('har -> dayjs, har开启bundledDependencies，需要收集dayjs/plugin/utc到extraImportees中', result);

  const har1BuildPackagePath = path.resolve(har1Root, BUILD_PACKAGE_JSON_PATH);
  expect(fse.existsSync(har1BuildPackagePath)).toEqual(true);
  let har1BuildPackageJson = getJson5Obj(har1BuildPackagePath);
  expect(har1BuildPackageJson.metadata.extraImportees.includes('dayjs')).toBeFalsy();
  expect(har1BuildPackageJson.metadata.extraImportees).toContain('dayjs/plugin/utc');
  await cleanTarget(project);

  // 2.验证har依赖dayjs，不开启bundledDependencies，不收集dayjs到extraImportees中
  await initPackageAndProfile(har1Root, {});
  const result1 = await runHvigorAsync(projectRoot, commands);
  record.addStep('har -> dayjs, har不开启bundledDependencies，需要收集dayjs/plugin/utc到extraImportees中', result1);
  expect(fse.existsSync(har1BuildPackagePath)).toEqual(true);
  har1BuildPackageJson = getJson5Obj(har1BuildPackagePath);
  expect(har1BuildPackageJson.metadata.extraImportees.includes('dayjs')).toBeFalsy();
  expect(har1BuildPackageJson.metadata.extraImportees).toContain('dayjs/plugin/utc');
  await cleanTarget(project);

  const harRoot = project.getModule('staticLibrary').moduleRootPath();
  const harMainPagePath = path.resolve(harRoot, 'src/main/ets/components/mainpage/MainPage.ets');
  await commonFileOperation(harMainPagePath, async (buffer) => {
    return `import { MainPage as MainPage1 } from 'staticLibrary1';\n${buffer
      .toString()
      .replace('.fontWeight(FontWeight.Bold)', '.fontWeight(FontWeight.Bold)\nMainPage1()')}`;
  });

  await validateChainHarHar1Dayjs(project, record);
  await validateChainHarByteHar1Dayjs(project, record);
  await validateChainHarHspDayjs(project, record);
};

enum deptTypes {
  HAR = 'har',
  BYTE_HAR = 'byteHar',
  HSP = 'hsp',
}
/**
 * har -> har1（源码HAR/字节码HAR） -> dayjs
 */
async function validateChainHarHar1Dayjs(project: Project, record: ICaseRecord, deptType: deptTypes = deptTypes.HAR) {
  const harRoot = project.getModule('staticLibrary').moduleRootPath();

  // 3.har -> har1（源码HAR） -> dayjs har开启bundledDependencies，需要收集dayjs到extraImportees中，需要收集har1到compiledPackageNames中
  // 5.har -> har1（字节码HAR） -> dayjs har开启bundledDependencies，不需要收集dayjs到extraImportees中，不需要收集har1到compiledPackageNames中
  // 7.har -> hsp -> dayjs har开启bundledDependencies，不需要收集dayjs到extraImportees中，不需要收集hsp到compiledPackageNames中
  const fileMap = { [deptTypes.HAR]: '../staticLibrary1', [deptTypes.BYTE_HAR]: './staticLibrary1.har', [deptTypes.HSP]: '../shareLibrary' };
  const deptName = deptType === deptTypes.HSP ? 'shareLibrary' : 'staticLibrary1';
  await initPackageAndProfile(harRoot, { arkOptions: { bundledDependencies: true } }, { [deptName]: `file:${fileMap[deptType]}` });
  // 执行ohpm install
  const install = await execa.command(commonConfig.ohpmInstallCommand, {
    cwd: harRoot,
  });
  record.addStep('har1 install', install);
  const commands = '--mode module -p module=staticLibrary@default assembleHar -p buildMode=release'.split(' ');
  const result = await runHvigorAsync(project.projectRootPath(), commands);
  const stepMsgMap = {
    [deptTypes.HAR]: 'har -> har1（源码HAR） -> dayjs har开启bundledDependencies，dayjs/plugin/utc需要收集到extraImportees中，需要收集har1到compiledPackageNames中',
    [deptTypes.BYTE_HAR]:
      'har -> har1（字节码HAR） -> dayjs har开启bundledDependencies，不需要收集dayjs/plugin/utc到extraImportees中，不需要收集har1到compiledPackageNames中',
    [deptTypes.HSP]: 'har -> hsp -> dayjs har开启bundledDependencies，不需要收集dayjs/plugin/utc到extraImportees中，不需要收集hsp到compiledPackageNames中',
  };
  record.addStep(stepMsgMap[deptType], result);

  const har1BuildPackagePath = path.resolve(harRoot, BUILD_PACKAGE_JSON_PATH);
  expect(fse.existsSync(har1BuildPackagePath)).toEqual(true);
  let harBuildPackageJson = getJson5Obj(har1BuildPackagePath);
  if (deptType === 'har') {
    expect(harBuildPackageJson.metadata.extraImportees.includes('dayjs')).toBeFalsy();
    expect(harBuildPackageJson.metadata.extraImportees).toContain('dayjs/plugin/utc');
    expect(harBuildPackageJson.metadata.compiledPackageNames).toContain('staticLibrary1');
  } else {
    expect(harBuildPackageJson.metadata.extraImportees.length).toBe(0);
    expect(harBuildPackageJson.metadata.compiledPackageNames.length).toBe(0);
  }
  await cleanTarget(project);

  // 4.har -> har1（源码HAR） -> dayjs 不开启bundledDependencies，不需要收集dayjs到extraImportees中，不需要收集har1到compiledPackageNames中
  // 6.har -> har1（字节码HAR） -> dayjs 不开启bundledDependencies，不需要收集dayjs到extraImportees中，不需要收集har1到compiledPackageNames中
  // 8.har -> hsp -> dayjs 不开启bundledDependencies，不需要收集dayjs到extraImportees中， 不需要收集hsp到compiledPackageNames中
  await initPackageAndProfile(harRoot, {});
  // 执行ohpm install
  const install1 = await execa.command(commonConfig.ohpmInstallCommand, {
    cwd: harRoot,
  });
  record.addStep('har1 install', install1);

  const result1 = await runHvigorAsync(project.projectRootPath(), commands);
  const msgMap = {
    [deptTypes.HAR]: 'har -> har1（源码HAR） -> dayjs 不开启bundledDependencies，不需要收集dayjs/plugin/utc到extraImportees中，不需要收集har1到compiledPackageNames中',
    [deptTypes.BYTE_HAR]:
      'har -> har1（字节码HAR） -> dayjs 不开启bundledDependencies，不需要收集dayjs/plugin/utc到extraImportees中，不需要收集har1到compiledPackageNames中',
    [deptTypes.HSP]: 'har -> hsp -> dayjs 不开启bundledDependencies，不需要收集dayjs/plugin/utc到extraImportees中， 不需要收集hsp到compiledPackageNames中',
  };
  record.addStep(msgMap[deptType], result1);
  expect(fse.existsSync(har1BuildPackagePath)).toEqual(true);
  harBuildPackageJson = getJson5Obj(har1BuildPackagePath);
  // 不开启bundledDependencies，不收集
  expect(harBuildPackageJson.metadata.extraImportees).toBe(undefined);
  expect(harBuildPackageJson.metadata.compiledPackageNames).toBe(undefined);
  await cleanTarget(project);
}

/**
 * har -> har1（字节码HAR） -> dayjs
 */
async function validateChainHarByteHar1Dayjs(project: Project, record: ICaseRecord) {
  const har1Root = project.getModule('staticLibrary1').moduleRootPath();
  await initPackageAndProfile(har1Root, { arkOptions: { byteCodeHar: true } });

  const commands = '--mode module -p module=staticLibrary1@default assembleHar -p buildMode=release'.split(' ');
  const result = await runHvigorAsync(project.projectRootPath(), commands);
  record.addStep('编译字节码har', result);
  const har1OutPut = path.resolve(har1Root, 'build/default/outputs/default/staticLibrary1.har');
  fse.copyFileSync(har1OutPut, path.resolve(project.getModule('staticLibrary').moduleRootPath(), 'staticLibrary1.har'));
  // 复用 har -> har1（源码HAR） -> dayjs
  await validateChainHarHar1Dayjs(project, record, deptTypes.BYTE_HAR);
}

async function validateChainHarHspDayjs(project: Project, record: ICaseRecord) {
  const hspRoot = project.getModule('shareLibrary').moduleRootPath();
  await initPackageAndProfile(hspRoot, {}, { dayjs: 'latest' });
  // 执行ohpm install
  const install = await execa.command(commonConfig.ohpmInstallCommand, {
    cwd: hspRoot,
  });
  record.addStep('hsp install', install);

  const hspIndex = path.resolve(hspRoot, 'Index.ets');
  await commonFileOperation(hspIndex, async (buffer) => {
    return `import dayjs from 'dayjs';\n${buffer}\nexport function getDayjsType() { return typeof dayjs }`;
  });

  const harRoot = project.getModule('staticLibrary').moduleRootPath();
  const harMainPagePath = path.resolve(harRoot, 'src/main/ets/components/mainpage/MainPage.ets');
  await fse.writeFile(
    harMainPagePath,
    `import {getDayjsType} from "shareLibrary";\n@Component\nexport struct MainPage {
  @State message: string = 'Hello World' + getDayjsType();
  build() { Row() { Column() { Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)}.width('100%') }.height('100%') }}`,
  );
  await validateChainHarHar1Dayjs(project, record, deptTypes.HSP);
}

TestCaseBuilder.builder()
  .caseName('DTS2024110609584')
  .description('多个har打包成一个har')
  .projectBuilderOption(projectBuilderOption)
  .taskConfig(TestTaskOptionBuilder.builder().caseLevel(CaseLevel.DTS).build())
  .testFun(testFun)
  .load();
